package nqueens.algorithm;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import nqueens.config.GAProperties;
import nqueens.core.Chromosome;
import nqueens.core.Population;

/**
 * Class that implements the selection operator
 * 
 * @author Artur Alves
 * @author Danilo Torres
 * @author Rodrigo Bruno
 * 
 * 
 */
public class SelectionDefault implements ISelectionAlgorithm {
	
	static class Pair {
		private Chromosome chromo;
		private double objective;
		
		public Pair(Chromosome ind, double obj) {
			super();
			this.chromo = ind;
			this.objective = obj;
		}

		public Chromosome getChromo() {
			return chromo;
		}

		public void setChromo(Chromosome ind) {
			this.chromo = ind;
		}

		public double getObjective() {
			return objective;
		}

		public void setObjective(double obj) {
			this.objective = obj;
		}
		
	}
	
	private GAProperties params;
	
	private ObjectiveCalculator calculator;
	
	public SelectionDefault() {
		params = GAProperties.getInstance();
		calculator = new ObjectiveCalculator();
	}

	@Override
	public Population initPopulation() {
		int n = params.getInteger(GAProperties.POPULATION_SIZE);
		int numQueens = params.getInteger(GAProperties.NUM_QUEENS);
		Population p = new Population();
		for (int i = 0; i < n; i++) {
			p.add(Chromosome.generateRandom(numQueens));
		}
		return p;
	}

	@Override
	public Population select(Population population) {
		List<Pair> pairs = makeObjChromoPairs(population);
		int n = params.getInteger(GAProperties.POPULATION_SIZE);
		Population newPop = new Population();
		for (int i = 0; i < n; i++) {
			newPop.add(selectRandomChromo(pairs));
		}
		return newPop;
	}

	private Chromosome selectRandomChromo(List<Pair> pairs) {
		double prob = Math.random();
		double acum = 0.0;
		for (Pair p : pairs) {
			acum += p.getObjective();
			if (acum > prob) {
				return p.getChromo();
			}
		}
		System.err.println("Fail, prob = " + prob + ", acum = " + acum);
		return null;
	}

	private List<Pair> makeObjChromoPairs(Population population) {
		List<Pair> pairs = new LinkedList<Pair>();
		Iterator<Chromosome> it = population.iterator();
		double objTotal = 0.0;
		while (it.hasNext()) {
			Chromosome next = it.next();
			double objective = calculator.calcObjective(next);
			pairs.add(new Pair(next, objective));
			objTotal += objective;
		}
		// todos os valores das funcoes objetivo sao normalizados de 0 a 1
		for (Pair p : pairs) {
			p.setObjective(objTotal == 0 ? (1.0/pairs.size()) : p.getObjective() / objTotal);
		}
		return pairs;
	}
	
	
}
